package com.usoft.thrift.benchmark;

import com.usoft.thrift.benchmark.util.Utils;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Created by xinxingegeya on 16/2/25.
 */
public class ServerConfiguration implements Configuration {

    ServerTransport transport = ServerTransport.T_NONBLOCKING_SERVER_SOCKET;
    Server server = Server.T_NONBLOCKING_SERVER;
    SocketAddress address;
    ProtocolType protocolType = ProtocolType.T_COMPACT_PROTOCOL;
    boolean ssl;

    int maxWorkerThreads = Integer.MAX_VALUE;//hsha 服务器使用的最大线程数量
    int minWorkerThreads = 5;//hsha服务器的最小线程数量
    int stopTimeoutVal = 60;//hash超时时间
    TimeUnit stopTimeoutUnit = TimeUnit.SECONDS;//时间单位
    int workerThreads = 10;//服务器工作线程的数量

    int selectorThreads = 2;// threaded selector server  

    private ServerConfiguration() {
    }

    static Builder newBuilder() {
        return new Builder();
    }

    static class Builder
            extends AbstractConfigurationBuilder<ServerConfiguration> {
        private static final List<Param> PARAMS = supportedParams();

        private Builder() {
        }

        @Override
        protected ServerConfiguration newConfiguration() {
            return new ServerConfiguration();
        }

        @Override
        protected Collection<Param> getParams() {
            return PARAMS;
        }

        /**
         * 验证config的合法性
         * 
         * @param config
         * @return
         */
        @Override
        protected ServerConfiguration build0(ServerConfiguration config) {
            if (config.ssl && !config.transport.sslSupported) {
                throw new IllegalArgumentException("ssl unsupported with the "
                    + config.transport.name().toLowerCase() + " transport");
            }

            // Verify that the address type is correct for the transport type.
            config.transport.validateSocketAddress(config.address);

            Server[] nioServers = new Server[] { Server.T_NONBLOCKING_SERVER,
                Server.T_HS_HA_SERVER, Server.T_THREAD_SELECTOR_SERVER };

            Server[] ioServers = new Server[] { Server.T_SIMPLE_SERVER,
                Server.T_THREAD_POOL_SERVER };

            if (Arrays.asList(nioServers).contains(config.server)
                && config.transport != ServerTransport.T_NONBLOCKING_SERVER_SOCKET) {
                throw new IllegalArgumentException(
                    "服务器是非阻塞模式,请使用NIO的Transport");
            }

            if (Arrays.asList(ioServers).contains(config.server)
                && config.transport != ServerTransport.T_SERVER_SOCKET) {
                throw new IllegalArgumentException("服务器是阻塞模式,请使用OIO的Transport");
            }

            return config;
        }

        private static List<Param> supportedParams() {
            return Collections.unmodifiableList(
                new ArrayList<Param>(Arrays.asList(ServerParam.values())));
        }
    }

    enum ServerTransport {
        T_NONBLOCKING_SERVER_SOCKET(true, "服务器端的非阻塞套接字",
                SocketAddressValidator.INET),
        T_SERVER_SOCKET(true, "服务器端套接字", SocketAddressValidator.INET);

        private final boolean sslSupported;
        private final String description;
        private final SocketAddressValidator socketAddressValidator;

        ServerTransport(boolean sslSupported, String description,
                SocketAddressValidator socketAddressValidator) {
            this.sslSupported = sslSupported;
            this.description = description;
            this.socketAddressValidator = socketAddressValidator;
        }

        /**
         * Validates the given address for this transport.
         *
         * @throws IllegalArgumentException
         *         if the given address is invalid for this transport.
         */
        void validateSocketAddress(SocketAddress address) {
            if (!socketAddressValidator.isValidSocketAddress(address)) {
                throw new IllegalArgumentException(
                    "Invalid address " + address + " for transport " + this);
            }
        }

        static String getDescriptionString() {
            StringBuilder builder = new StringBuilder(
                "Select the transport to use. Options:\n");
            boolean first = true;
            for (ServerTransport transport : ServerTransport.values()) {
                if (!first) {
                    builder.append("\n");
                }
                builder.append(transport.name().toLowerCase());
                builder.append(": ");
                builder.append(transport.description);
                first = false;
            }
            return builder.toString();
        }
    }

    /**
     * server param
     * 枚举实现接口
     */
    enum ServerParam implements AbstractConfigurationBuilder.Param {
        ADDRESS("STR", "Socket address (host:port)", null, true) {
            @Override
            protected void setServerValue(ServerConfiguration config,
                    String value) {
                SocketAddress address = Utils.parseSocketAddress(value);
                if (address instanceof InetSocketAddress) {
                    InetSocketAddress addr = (InetSocketAddress) address;
                    int port = addr.getPort() == 0 ? Utils.pickUnusedPort()
                        : addr.getPort();
                    // Re-create the address so that the server is available on all local addresses.
                    address = new InetSocketAddress(port);
                }
                config.address = address;
            }
        },
        SERVER("STR", "thrift server", null, true) {
            @Override
            protected void setServerValue(ServerConfiguration config,
                    String value) {
                Server server = Enum.valueOf(Server.class, value);
                config.server = server;
            }
        },
        TRANSPORT("STR", "thrift transport", null, true) {
            @Override
            protected void setServerValue(ServerConfiguration config,
                    String value) {
                ServerTransport transport = Enum.valueOf(ServerTransport.class,
                    value);
                config.transport = transport;
            }
        },
        PROTOCOL("STR", "thrift protocol", null, true) {
            @Override
            protected void setServerValue(ServerConfiguration config,
                    String value) {
                ProtocolType protocolType = Enum.valueOf(ProtocolType.class,
                    value);
                config.protocolType = protocolType;
            }
        };

        private final String type;
        private final String description;
        private final String defaultValue;
        private final boolean required;

        ServerParam(String type, String description, String defaultValue) {
            this(type, description, defaultValue, false);
        }

        ServerParam(String type, String description, String defaultValue,
                boolean required) {
            this.type = type;
            this.description = description;
            this.defaultValue = defaultValue;
            this.required = required;
        }

        @Override
        public String getName() {
            return name().toLowerCase();
        }

        @Override
        public String getType() {
            return type;
        }

        @Override
        public String getDescription() {
            return description;
        }

        @Override
        public String getDefaultValue() {
            return defaultValue;
        }

        @Override
        public boolean isRequired() {
            return required;
        }

        @Override
        public void setValue(Configuration config, String value) {
            setServerValue((ServerConfiguration) config, value);
        }

        protected abstract void setServerValue(ServerConfiguration config,
                String value);
    }

}
